import Excel from 'exceljs'
import { NameRewriting, isVNode, queryVNodes, queryChildVNodes, getVNodeText } from './utils'

// 用于获取列名
const colIdSet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';

// 头部默认样式
const defaultTHCellStyle = {
  alignment: { vertical: 'middle', horizontal: 'center', wrapText: true },
  fill: {
    type: 'pattern',
    pattern: 'solid',
    fgColor: { argb: 'FFF4F5FA' },
  },
  border: {
    top: { style: 'thin', color: { argb: 'FFD4D4D4' } },
    left: { style: 'thin', color: { argb: 'FFD4D4D4' } },
    bottom: { style: 'thin', color: { argb: 'FFD4D4D4' } },
    right: { style: 'thin', color: { argb: 'FFD4D4D4' } },
  },
};

// 单元格默认样式
const defaultTdCellStyle = {
  alignment: { vertical: 'middle', horizontal: 'center', wrapText: true },
  fill: {
    type: 'pattern',
    pattern: 'solid',
    fgColor: { argb: 'FFFFFFFF' },
  },
  border: {
    top: { style: 'thin', color: { argb: 'FFD4D4D4' } },
    left: { style: 'thin', color: { argb: 'FFD4D4D4' } },
    bottom: { style: 'thin', color: { argb: 'FFD4D4D4' } },
    right: { style: 'thin', color: { argb: 'FFD4D4D4' } },
  },
};

export default class TableExporter {
  constructor(options) {
    if (!options) {
      console.error('TableExporter 参数错误');
      return;
    }
    this.tables =
      options.tables && (Array.isArray(options.tables) ? options.tables : [options.tables]);
  }

  async export () {
    if (!this.tables || !this.tables.length) {
      console.error('TableExporter export 参数错误');
      return;
    }
    // 创建工作簿属性
    this.workbook = new Excel.Workbook()

    const nameRewriting = new NameRewriting()
    for (let i in this.tables || []) {
      debugger
      let table = this.tables[i];
      let sheetName = table.sheetName || `Sheet${i + 1}`;
      sheetName = nameRewriting.get(sheetName) // 保证命名不重复、限制sheet长度
      console.log(sheetName, 'sheetNamesheetNamesheetName')
      let worksheet = this.workbook.addWorksheet(sheetName);

      // 从表格获取数据
      let datas = this.getDataFromTable(table);

      let rows = await this.getRows(datas, i);
      console.log('rows =====', rows);
      // 填充数据
      this.addRows(worksheet, rows);
      // 合并单元格
      this.mergeCells(worksheet, rows);

      // 设置单元格样式
      this.setCellStyle(worksheet, rows);
      // 设置列样式
      this.setColumnsStyle(worksheet, table.cloumnsStyle);
      // 设置行样式
      this.setRowsStyle(worksheet, rows);
    }
  }

  download (fileName) {
    return new Promise(async (resolve, reject) => {
      if (!this.workbook) {
        reject(new Error('TableExporter download 缺少 workbook'));
        return;
      }
      setTimeout(async () => {
        try {
          let FileSaver = await import('file-saver');
          let data = await this.workbook.xlsx.writeBuffer();
          const blob = new Blob([data], {
            type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8',
          });
          // Given name
          FileSaver.saveAs(blob, fileName);
          resolve();
        } catch (error) {
          reject(error);
        }
      }, 200);
    });
  }

  /**
   * @description: 获取 table 的数据
   * @param {Object} table 表格数据
   * @return: Array
   */
  getDataFromTable (table) {
    if (!table || !table.datas) {
      console.error('table 缺少 datas -->', table);
      return [];
    }

    if (!Array.isArray(table.datas)) {
      table.datas = [table.datas];
    }

    let result = [];
    (table.datas || []).forEach(item => {
      if (!item) {
        return;
      }

      if (isVNode(item)) {
        // 虚拟 dom
        result = result.concat(this.getDataFromVnode(item, table));
      } else if (typeof item === 'object') {
        if (Array.isArray(item)) {
          // 数组
          item.forEach(child => {
            if (isVNode(child)) {
              // 虚拟 dom
              result = result.concat(this.getDataFromVnode(child, table));
            } else if (typeof child === 'object' && !Array.isArray(child)) {
              result = result.concat([child]);
            }
          });
        } else {
          // 对象
          result = result.concat([item]);
        }
      }
    });

    return result;
  }

  /**
   * @description: 从虚拟 dom 获取数据
   * @param {Object} table 表格数据
   * @return: Array rows
   */
  getDataFromVnode (vnode, table) {
    if (!vnode || !isVNode(vnode)) {
      console.error('getDataFromVnode 参数错误');
      return [];
    }

    let result = [];
    let vnodes = Array.isArray(vnode) ? vnode : [vnode];

    vnodes.forEach(vnode => {
      if (typeof vnode !== 'object') {
        return;
      }
      let trNodes = queryVNodes(vnode, node => node.tag === 'tr');

      // 循环行
      trNodes.forEach((trNode, trIndex) => {
        let trData = {
          cells: [],
        };
        let tdNodes = queryChildVNodes(trNode, item => item.tag === 'td' || item.tag === 'th');

        // 循环单元格
        tdNodes.forEach((tdNode, tdIndex) => {
          let attrs = (tdNode.data && tdNode.data.attrs) || {};
          let text = getVNodeText(tdNode);
          let tag = tdNode.tag;
          let rowspan = Number(attrs.rowspan) || 1;
          let colspan = Number(attrs.colspan) || 1;
          let style =
            table.getCellStyle && table.getCellStyle(tdNode, tdIndex, tdNodes);

          trData.cells.push({ text, tag, rowspan, colspan, style });
        });

        trData.style =
          table.getRowStyle && table.getRowStyle(trNode, trIndex, trNodes);

        result.push(trData);
      });
    });

    return result;
  }

  /* 从 table 的 dom 获取数据 */
  async getRows (datas = [], sheetIndex) {
    let result = [];

    for (let rowI in datas) {
      let rowIndex = Number(rowI);
      let rowData = datas[rowIndex];
      let cells = rowData.cells;
      let rowResult = {
        cells: [],
        style: rowData.style,
      };

      for (let cellI in cells) {
        let cellIndex = Number(cellI);
        let cell = cells[cellIndex];
        let text = cell.text;
        let tag = cell.tag;
        let rowspan = cell.rowspan || 1;
        let colspan = cell.colspan || 1;

        // 当前“行”
        let row = this.getRowId(rowIndex);
        // 当前“列”
        // "列"的情况比较复杂，横向合并 和 纵向合并 都会导致列变化
        let realColIndex = this.getRealColIndex(rowIndex, cellIndex, sheetIndex);
        let col = this.getColId(realColIndex);
        let colIndex = realColIndex + 1;
        // 当前单元格id
        let cellId = col + row;
        // 合并单元的 end
        let mergeRow = this.getRowId(rowIndex + rowspan - 1);
        let mergeCol = this.getColId(realColIndex + colspan - 1);
        let mergeTo = mergeCol + mergeRow;
        let style = Object.assign(
          {},
          tag === 'th' ? defaultTHCellStyle : defaultTdCellStyle,
          cell.style
        );

        rowResult.cells.push({
          text,
          tag,
          row,
          rowIndex,
          col,
          colIndex,
          rowspan,
          colspan,
          cellId,
          mergeTo,
          style,
        });

        this.setFillCell(rowIndex, realColIndex, rowspan, colspan, sheetIndex);
      }

      result.push(rowResult);
    }

    return result;
  }

  /* 获取真实的列 index
   * 如果当前位置（列+行），已经被填充了，则继续往右找一格
   * 重复此过程，直到找到一个空的单元格
   */
  getRealColIndex (rowIndex, cellIndex, sheetIndex) {
    if (this.fillCell && this.fillCell[`${sheetIndex}-${rowIndex}-${cellIndex}`]) {
      return this.getRealColIndex(rowIndex, cellIndex + 1, sheetIndex);
    } else {
      return cellIndex;
    }
  }

  /* 记录已经填充的单元格，用于判断单元格的位置
   * 格式为对象，方便查询
   * {
   *    0-1-1: 1,
   *    0-1-2: 1,
   *    sheetIndex-rowIndex-colIndex: 1
   * }
   */
  setFillCell (rowIndex, cellIndex, rowspan = 1, colspan = 1, sheetIndex) {
    this.fillCell = this.fillCell || {};

    for (let rowI = 0; rowI < rowspan; rowI++) {
      for (let colI = 0; colI < colspan; colI++) {
        this.fillCell[`${sheetIndex}-${rowIndex + rowI}-${cellIndex + colI}`] = 1;
      }
    }
  }

  /* 获取“列”id */
  getColId (index) {
    if (Math.floor(index / 26) < 1) {
      return colIdSet.substr(index, 1);
    } else {
      let digitsIndex = index % 26;
      let digitsId = colIdSet.substr(digitsIndex, 1);
      let upDigitsId = this.getColId(Math.floor(index / 26) - 1);
      return upDigitsId + digitsId;
    }
  }

  /* 获取“行”id */
  getRowId (index) {
    return index + 1;
  }

  addRows (worksheet, rows) {
    rows.forEach(row => {
      this.addRow(worksheet, row);
    });
  }

  addRow (worksheet, row) {
    let data = [];
    row.cells.forEach(cell => {
      // 如果内容是一个数字，则转成数字格式，导出的单元格也会变成数字格式
      let text = cell.text && cell.text.trim && cell.text.trim();
      text = (text === undefined || text === null || text === '' ? '' : (isNaN(text) ? text : parseFloat(text)));
      data[cell.colIndex] = text;
    });
    worksheet.addRow(data);
  }

  /* 合并单元格
   * 对比单元格的 cellId，和 mergeTo 对比，如果不一样，则表示要合并
   * mergeTo 表示要合并的“终点”，即右下角
   */
  mergeCells (worksheet, rows) {
    rows.forEach(row => {
      row.cells.forEach(cell => {
        if (cell && cell.cellId && cell.mergeTo && cell.cellId !== cell.mergeTo) {
          worksheet.mergeCells([cell.cellId, cell.mergeTo]);
        }
      });
    });
  }

  /* 设置单元格样式
   */
  setCellStyle (worksheet, rows) {
    rows.forEach(row => {
      row.cells.forEach(td => {
        let cell = worksheet.getCell(td.cellId);
        let style = td.style;

        if (style) {
          for (let styleName in style) {
            cell[styleName] = style[styleName];
          }
        }
      });
    });
  }

  /* 设置列样式
   * 注：width 的单位为“字”，一个中文算为2个“字”
   */
  setColumnsStyle (worksheet, cloumnsStyle = []) {
    cloumnsStyle.forEach((style, cloumnIndex) => {
      let cloumn = worksheet.getColumn(cloumnIndex + 1);

      for (let key in style) {
        cloumn[key] = style[key];
      }
    });
  }

  /* 设置行样式 */
  setRowsStyle (worksheet, rows = []) {
    if (!worksheet) {
      return;
    }

    rows.forEach((item, index) => {
      let row = worksheet.getRow(index + 1);
      let style = item.style;

      if (!style) {
        return;
      }

      for (let key in style) {
        row[key] = style[key];
      }
    });
  }
}